import os
import tempfile
import zipfile
import shutil
from typing import Dict, Any, List, Optional, Union
from datetime import datetime
import re

import easyocr
from openai import OpenAI, APIError
from PyPDF2 import PdfReader
from docx import Document
import numpy as np
import cv2
from fastapi import FastAPI, File, UploadFile, HTTPException
from fastapi.responses import JSONResponse
from pydantic import BaseModel
import uvicorn


class ContractAnalysisResponse(BaseModel):
    文件类型: str
    主体公司名称: str
    合同起始日期: str
    合同有效期: str
    约定薪资: str
    关键信息摘要: str
    文件有效性说明: str
    与案件关联性分析: str
    是否可以作为核心证据: str


def classify_contract_type(text: str) -> str:
    """分类合同类型"""
    categories = {
        # 按期限
        "固定期限劳动合同": ["合同期限自", "为期", "终止时间"],
        "无固定期限劳动合同": ["无固定期限", "不设终止日期", "除非双方解除"],
        "以完成工作任务为期限劳动合同": ["任务完成之日", "项目结束即终止", "工作成果"],
        # 按工作时间强度
        "全日制劳动合同": ["标准工时制", "五天八小时", "每日工作时长"],
        "非全日制用工合同": ["非全日制", "小时工", "计时工", "累计工作时间"],
        "零工/临时工合同": ["临时", "一次性任务", "单次派工", "劳务报酬"],
        # 按用工主体
        "劳务派遣合同": ["派遣公司", "派遣单位", "劳务派遣", "第三方用工"],
        "劳务外包合同": ["承揽", "外包", "服务费"],
        # 按身份资历
        "实习/见习协议": ["实习", "见习", "学校三方协议", "实习期间"],
        "试用期协议": ["试用期", "不符合录用条件", "提前通知解除"],
        "顾问/专家聘用合同": ["顾问", "专家", "咨询服务", "顾问费"],
        "兼职协议": ["兼职", "兼任", "工作时间安排"],
        # 专项条款（独立或附属）
        "保密协议（NDA）": ["保密协议", "保密信息", "违约责任", "保密期限"],
        "竞业限制协议": ["竞业限制", "竞业限制期", "经济补偿", "地域范围"],
        "员工持股/股权激励协议": ["股权激励", "员工持股", "认购价格", "归属期限"],
        "劳动争议调解/赔偿协议": ["争议解决", "和解", "一次性赔偿", "调解"],
        # 新兴用工模式
        "平台经济用工协议": ["平台", "派单", "灵活用工", "平台与劳动者"],
        "远程/异地用工合同": ["远程办公", "异地", "通信工具", "工作地点"],
        "弹性用工协议": ["弹性工时", "核心工作时段", "自主排班"],
    }

    for name, keywords in categories.items():
        if any(keyword in text for keyword in keywords):
            return name
    return "劳动合同"


def detect_seal_in_text(text: str) -> bool:
    """通过关键字判断 OCR 文本中是否有'公章'、'盖章'等字样。"""
    seal_keywords = ["公章", "盖章", "（章）", "企业章", "法人章"]
    return any(k in text for k in seal_keywords)


def detect_seal_in_image(file_path: str, min_area_ratio: float = 0.001) -> bool:
    """
    对单张图片做红色圆形印章的更严格检测：
    - 颜色分割后要求红色区域占比超过 min_area_ratio
    - Hough 圆检测验证圆形结构
    """
    if not os.path.exists(file_path):
        return False
    try:
        arr = np.fromfile(file_path, dtype=np.uint8)
        img = cv2.imdecode(arr, cv2.IMREAD_COLOR)
        if img is None:
            return False
        hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
        # 红色在 HSV 空间范围
        lower1, upper1 = np.array([0, 70, 50]), np.array([10, 255, 255])
        lower2, upper2 = np.array([170, 70, 50]), np.array([180, 255, 255])
        mask = cv2.inRange(hsv, lower1, upper1) | cv2.inRange(hsv, lower2, upper2)
        # 计算红色像素比例
        red_ratio = np.sum(mask > 0) / (mask.size + 1e-6)
        if red_ratio < min_area_ratio:
            return False
        # 模糊后找圆
        blurred = cv2.GaussianBlur(mask, (9, 9), 2)
        circles = cv2.HoughCircles(
            blurred, cv2.HOUGH_GRADIENT, dp=1.2, minDist=100,
            param1=50, param2=35, minRadius=30, maxRadius=150
        )
        return circles is not None
    except Exception:
        return False


class ContractAnalyzer:
    def __init__(self, api_key: str):
        self.client = OpenAI(
            api_key=api_key,
            base_url="https://api.deepseek.com"
        )
        self.ocr_reader = None

    def _get_ocr_reader(self):
        """延迟初始化OCR读取器"""
        if self.ocr_reader is None:
            self.ocr_reader = easyocr.Reader(['ch_sim', 'en'])
        return self.ocr_reader

    def _extract_from_image(self, file_path: str) -> str:
        """从图片中提取文本"""
        try:
            if not os.path.exists(file_path):
                raise FileNotFoundError(f"文件不存在: {file_path}")
            img_array = np.fromfile(file_path, dtype=np.uint8)
            image = cv2.imdecode(img_array, cv2.IMREAD_COLOR)
            if image is None:
                raise ValueError("无法解析图像，cv2.imdecode 失败")
            reader = self._get_ocr_reader()
            result = reader.readtext(image, detail=0)
            return "\n".join(result).strip()
        except Exception as e:
            raise RuntimeError(f"图片OCR识别失败: {str(e)}")

    def _extract_from_pdf(self, file_path: str) -> str:
        """从PDF中提取文本"""
        text = ""
        try:
            with open(file_path, 'rb') as file:
                reader = PdfReader(file)
                for page in reader.pages:
                    text += page.extract_text() or ""
        except Exception as e:
            raise RuntimeError(f"PDF文件读取失败: {str(e)}")
        return text

    def _extract_from_docx(self, file_path: str) -> str:
        """从DOCX中提取文本"""
        try:
            doc = Document(file_path)
            return "\n".join([para.text for para in doc.paragraphs if para.text])
        except Exception as e:
            raise RuntimeError(f"DOCX文件读取失败: {str(e)}")

    def extract_text_from_file(self, file_path: str) -> str:
        """根据文件类型提取文本"""
        if not os.path.exists(file_path):
            raise FileNotFoundError(f"文件不存在: {file_path}")
        
        ext = file_path.lower()
        if ext.endswith('.pdf'):
            return self._extract_from_pdf(file_path)
        elif ext.endswith('.docx'):
            return self._extract_from_docx(file_path)
        elif ext.endswith(('.png', '.jpg', '.jpeg', '.bmp', '.tiff', '.webp')):
            return self._extract_from_image(file_path)
        else:
            raise ValueError("不支持的文件格式，请提供PDF、DOCX或图片文件")

    def extract_texts_from_multiple_images(self, image_paths: List[str]) -> str:
        """从多张图片中提取文本"""
        all_text = []
        for path in image_paths:
            try:
                text = self._extract_from_image(path)
                all_text.append(text)
            except Exception as e:
                print(f"⚠️ 图片处理失败 [{path}]: {str(e)}")
        return "\n\n".join(all_text)

    def _extract_structured_info(self, analysis_text: str, original_text: str) -> Dict[str, str]:
        """从分析结果中提取结构化信息"""
        # 使用正则表达式和关键词匹配提取信息
        result = {
            "文件类型": "劳动合同",
            "主体公司名称": "未识别",
            "合同起始日期": "未识别",
            "合同有效期": "未识别",
            "约定薪资": "未识别",
            "关键信息摘要": "未识别",
            "文件有效性说明": "未识别",
            "与案件关联性分析": "可证明双方存在劳动关系",
            "是否可以作为核心证据": "待确认"
        }

        # 提取公司名称
        company_patterns = [
            r'([\u4e00-\u9fa5]+(?:有限公司|股份有限公司|集团|公司))',
            r'甲方[：:](.*?)(?:\n|乙方)',
            r'用人单位[：:](.*?)(?:\n|劳动者)'
        ]
        for pattern in company_patterns:
            match = re.search(pattern, original_text)
            if match:
                result["主体公司名称"] = match.group(1).strip()
                break

        # 提取合同日期
        date_patterns = [
            r'(\d{4}年\d{1,2}月\d{1,2}日)',
            r'(\d{4}-\d{1,2}-\d{1,2})',
            r'(\d{4}\.\d{1,2}\.\d{1,2})'
        ]
        for pattern in date_patterns:
            match = re.search(pattern, original_text)
            if match:
                result["合同起始日期"] = match.group(1)
                break

        # 提取合同期限
        duration_patterns = [
            r'合同期限[：:]?([^\n]*?)(?:年|月)',
            r'期限[：:]?([^\n]*?)(?:年|月)',
            r'有效期[：:]?([^\n]*?)(?:年|月)'
        ]
        for pattern in duration_patterns:
            match = re.search(pattern, original_text)
            if match:
                result["合同有效期"] = match.group(1).strip() + "年"
                break

        # 提取薪资信息
        salary_patterns = [
            r'(\d+(?:,\d{3})*(?:\.\d+)?)[元]?[/／]?月',
            r'月薪[：:]?(\d+(?:,\d{3})*(?:\.\d+)?)',
            r'工资[：:]?(\d+(?:,\d{3})*(?:\.\d+)?)'
        ]
        for pattern in salary_patterns:
            match = re.search(pattern, original_text)
            if match:
                result["约定薪资"] = match.group(1) + "元/月"
                break

        # 生成摘要信息
        if "试用期" in original_text:
            result["关键信息摘要"] = "合同包含试用期条款"
        if "违约金" in original_text:
            result["关键信息摘要"] += "，约定违约金条款"
        if result["关键信息摘要"] == "未识别":
            result["关键信息摘要"] = "标准劳动合同条款"

        # 判断文件有效性
        has_seal = detect_seal_in_text(original_text)
        if has_seal:
            result["文件有效性说明"] = "合同已双方签字盖章，具备法律效力"
            result["是否可以作为核心证据"] = "是"
        else:
            result["文件有效性说明"] = "合同签字情况需进一步确认"
            result["是否可以作为核心证据"] = "待确认"

        return result

    def analyze_contract(self, text: str, file_paths: Optional[List[str]] = None) -> Dict[str, str]:
        """分析合同并返回结构化结果"""
        if not text.strip():
            raise ValueError("合同文本内容为空")

        # 检测印章
        has_seal_text = detect_seal_in_text(text)
        has_seal_image = False
        if file_paths:
            for p in file_paths:
                if p.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.tiff')) and detect_seal_in_image(p):
                    has_seal_image = True
                    break

        seal_note = "（检测到公章）" if has_seal_text or has_seal_image else "（未检测到公章）"
        contract_type = classify_contract_type(text)

        # 构建提示词，要求返回结构化信息
        system_prompt = f"""你是一位专业的劳动法律师，负责分析劳动合同的合规性。
该合同初步识别为：{contract_type} {seal_note}

请分析合同内容，并严格按照以下JSON格式返回结构化信息：
{{
  "文件类型": "具体的合同类型",
  "主体公司名称": "用人单位名称",
  "合同起始日期": "YYYY年MM月DD日格式",
  "合同有效期": "X年或具体期限",
  "约定薪资": "XXXX元/月",
  "关键信息摘要": "包含试用期、违约金等关键条款的简要说明",
  "文件有效性说明": "基于{seal_note}的有效性判断",
  "与案件关联性分析": "该合同在劳动纠纷中的证据价值",
  "是否可以作为核心证据": "是/否/待确认"
}}

请仔细分析合同内容，提取准确信息。如果某项信息无法确定，请标注为"未明确"。"""

        try:
            response = self.client.chat.completions.create(
                model="deepseek-chat",
                messages=[
                    {"role": "system", "content": system_prompt},
                    {"role": "user", "content": f"请分析以下劳动合同：\n{text[:15000]}"}
                ],
                temperature=0.3,
                max_tokens=1500
            )
            
            analysis_result = response.choices[0].message.content
            
            # 尝试解析JSON响应，如果失败则使用备用提取方法
            try:
                import json
                # 提取JSON部分
                json_start = analysis_result.find('{')
                json_end = analysis_result.rfind('}') + 1
                if json_start != -1 and json_end != -1:
                    json_str = analysis_result[json_start:json_end]
                    return json.loads(json_str)
            except:
                pass
            
            # 备用方法：从分析文本中提取结构化信息
            return self._extract_structured_info(analysis_result, text)
            
        except APIError as e:
            raise RuntimeError(f"API请求失败: {str(e)}")
        except Exception as e:
            raise RuntimeError(f"分析过程中出错: {str(e)}")


# FastAPI应用
app = FastAPI(title="劳动合同分析API", description="分析劳动合同文件并返回结构化信息")

# 初始化分析器
API_KEY = "sk-20856422ed6644e3827b9d5403c9542a"  # 请替换为实际的API密钥
analyzer = ContractAnalyzer(API_KEY)


@app.post("/analyze_contract", response_model=ContractAnalysisResponse)
async def analyze_contract_endpoint(file: UploadFile = File(...)):
    """
    分析劳动合同文件
    
    支持的文件格式：
    - PDF文件
    - DOCX文件
    - 图片文件（JPG, PNG, BMP, TIFF等）
    - ZIP压缩包（包含多张图片）
    """
    temp_dir = None
    try:
        # 验证文件类型
        allowed_extensions = {'.pdf', '.docx', '.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.webp', '.zip'}
        file_ext = os.path.splitext(file.filename.lower())[1]
        
        if file_ext not in allowed_extensions:
            raise HTTPException(
                status_code=400, 
                detail=f"不支持的文件格式: {file_ext}。支持的格式: {', '.join(allowed_extensions)}"
            )
        
        # 创建临时目录
        temp_dir = tempfile.mkdtemp()
        
        # 保存上传的文件
        temp_file_path = os.path.join(temp_dir, file.filename)
        with open(temp_file_path, "wb") as buffer:
            content = await file.read()
            buffer.write(content)
        
        file_paths = []
        extracted_text = ""
        
        if file_ext == '.zip':
            # 处理ZIP文件
            extract_dir = os.path.join(temp_dir, "extracted")
            os.makedirs(extract_dir, exist_ok=True)
            
            with zipfile.ZipFile(temp_file_path, 'r') as zip_ref:
                zip_ref.extractall(extract_dir)
            
            # 收集所有图片文件
            image_extensions = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.webp'}
            for root, dirs, files in os.walk(extract_dir):
                for f in files:
                    if os.path.splitext(f.lower())[1] in image_extensions:
                        file_paths.append(os.path.join(root, f))
            
            if not file_paths:
                raise HTTPException(status_code=400, detail="ZIP文件中未找到支持的图片文件")
            
            # 从多张图片提取文本
            extracted_text = analyzer.extract_texts_from_multiple_images(file_paths)
            
        else:
            # 处理单个文件
            file_paths = [temp_file_path]
            extracted_text = analyzer.extract_text_from_file(temp_file_path)
        
        if not extracted_text.strip():
            raise HTTPException(status_code=400, detail="未能从文件中提取到有效文本内容")
        
        # 分析合同
        analysis_result = analyzer.analyze_contract(extracted_text, file_paths)
        
        # 对敏感信息进行脱敏处理
        if "主体公司名称" in analysis_result and analysis_result["主体公司名称"] != "未识别":
            company_name = analysis_result["主体公司名称"]
            if len(company_name) > 4:
                # 保留前2个和后2个字符，中间用*替代
                analysis_result["主体公司名称"] = company_name[:2] + "*" * (len(company_name) - 4) + company_name[-2:]
        
        return ContractAnalysisResponse(**analysis_result)
        
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"处理文件时发生错误: {str(e)}")
    
    finally:
        # 清理临时文件
        if temp_dir and os.path.exists(temp_dir):
            try:
                shutil.rmtree(temp_dir)
            except Exception:
                pass


@app.get("/health")
async def health_check():
    """健康检查接口"""
    return {"status": "healthy", "timestamp": datetime.now().isoformat()}


if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8001)